<?php


namespace BoojasrFramework\ServicesContainer;


use BoojasrFramework\Controller\ControllerResolver;
use BoojasrFramework\Response\Response;
use BoojasrFramework\ServicesContainer\Event\ControllerEvent;
use BoojasrFramework\ServicesContainer\Event\FilterControllerArgumentsEvent;
use BoojasrFramework\ServicesContainer\Event\FinishRequestEvent;
use BoojasrFramework\ServicesContainer\Event\GetResponseEvent;
use BoojasrFramework\ServicesContainer\Event\ResponseEvent;
use BoojasrFramework\ServicesContainer\Event\TerminateEvent;
use BoojasrFramework\ServicesContainer\EventDispatcher\EventDispatcherInterface;
use BoojasrFramework\ServicesContainer\Exception\RequestException;

class HttpKernel implements HttpKernelInterface,TerminableInterface
{

    /** @var EventDispatcherInterface $dispatcher */
    protected $dispatcher;

    /** @var RequestStack $requestStack */
    private $requestStack;

    /** @var ControllerResolver $resolver */
    private $resolver;

    public function __construct($dispatcher)
    {
        $this->dispatcher = $dispatcher;
    }

    public function handle($request, $type = HttpKernelInterface::MASTER_REQUEST, $catch = true)
    {
        $request->headers->set('X-Php-Ob-Level', ob_get_level());
        return $this->handleRaw($request, $type);
    }

    /**
     * @throws RequestException
     */
    private function handleRaw($request, $type = self::MASTER_REQUEST)
    {
        $this->requestStack->push($request);

        // request
        $requestEvent = new GetResponseEvent($this, $request, $type);
        $this->dispatcher->dispatch(KernelEvents::REQUEST, $requestEvent);
        if ($requestEvent->hasResponse()){
            return $this->filterResponse($requestEvent->getResponse(), $request, $type);
        }
        // load controller
        if (false === $controller = $this->resolver->getController($request)) {
            throw new \Exception(sprintf('Unable to find the controller for path "%s". The route is wrongly configured.', $request->getPathInfo()));
        }

        $controllerEvent = new ControllerEvent($this,$controller,$request,$type);
        $this->dispatcher->dispatch(KernelEvents::CONTROLLER,$controllerEvent );
        $controller = $controllerEvent->getController();

        // controller arguments
        $arguments = $this->resolver->getArguments($request, $controller);

        $event = new FilterControllerArgumentsEvent($this, $controller, $arguments, $request, $type);
        $this->dispatcher->dispatch(KernelEvents::CONTROLLER_ARGUMENTS, $event);
        $controller = $event->getController();
        $arguments = $event->getArguments();

        if ($controller&& is_callable($controller)){
            $response = call_user_func($controller,...$arguments);
            if ($response instanceof Response){
                return $this->filterResponse($response, $request, $type);
            }
        }

        throw new RequestException("控制器处理异常");
    }

    /**
     * Filters a response object.
     *
     * @throws \RuntimeException if the passed object is not a Response instance
     */
    private function filterResponse(Response $response, Request $request, int $type): Response
    {
        $event = new ResponseEvent($this, $request, $type, $response);

        $this->dispatcher->dispatch(KernelEvents::RESPONSE,$event);

        $this->finishRequest($request, $type);

        return $event->getResponse();
    }


    public function finishRequest($request, $type=HttpKernelInterface::MASTER_REQUEST)
    {
        $this->dispatcher->dispatch(KernelEvents::FINISH_REQUEST, new FinishRequestEvent($this, $request, $type));
        $this->requestStack->pop();
    }

    public function terminate(Request $request, Response $response)
    {
        $this->dispatcher->dispatch(KernelEvents::TERMINATE,new TerminateEvent($this, $request, $response));
    }
}
